{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9593d945",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1a63879e",
   "metadata": {},
   "outputs": [],
   "source": [
    "my_arr = np.arange(1_000_000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f7d298e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "my_list = list(range(1_000_000))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "36deb64d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: total: 15.6 ms\n",
      "Wall time: 12 ms\n"
     ]
    }
   ],
   "source": [
    "%time for _ in range(10): my_arr2 = my_arr * 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "54e50d1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: total: 531 ms\n",
      "Wall time: 529 ms\n"
     ]
    }
   ],
   "source": [
    "%time for _ in range(10): my_list2 = [x * 2 for x in my_list]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0f367d28",
   "metadata": {},
   "outputs": [],
   "source": [
    "# the numpy ndarray: A Multidimensional Array Object\n",
    "# N-dimensional array object or ndarray\n",
    "# Arrays enable you to perform mathematical operations on whole blocks of data using similar syntax to \n",
    "# the equivalent operations between scalar elements.\n",
    "import numpy as np\n",
    "# Generate some random data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6c143a95",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = np.random.randn(2,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "65d8ec8c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.27794963,  0.31835029,  0.3073955 ],\n",
       "       [-0.79108562, -2.69995657,  0.45262317]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e285b03b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ -2.77949633,   3.18350292,   3.07395503],\n",
       "       [ -7.91085616, -26.9995657 ,   4.52623174]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# mathematical operations with data\n",
    "# all of the elements have been multiplied by  10\n",
    "data * 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "7f2afdd6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.55589927,  0.63670058,  0.61479101],\n",
       "       [-1.58217123, -5.39991314,  0.90524635]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# corresponding values in each \"cell\" in the array have been added each other.\n",
    "data + data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "1ad82563",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# An ndarray 是一个用于存储同质数据的通用多维容器（all of the elements must be the same type）\n",
    "# Every array has a shape and dtype\n",
    "# a shape, a tuple indicating the size of each dimension\n",
    "data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b4aef9aa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a dtype, an object describing the data type of the array\n",
    "data.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "effb6071",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([6. , 7.5, 8. , 0. , 1. ])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# creating ndarrays\n",
    "# use array function to create an array\n",
    "# accept any sequence-like object\n",
    "# a list is a good candidate for conversion\n",
    "data1 = [6, 7.5, 8, 0, 1]\n",
    "arr1 = np.array(data1)\n",
    "arr1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "306d392d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3, 4],\n",
       "       [5, 6, 7, 8]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Nested sequences, like a list of equal-length lists\n",
    "# convert into a multidimensional array\n",
    "data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]\n",
    "arr2 = np.array(data2)\n",
    "arr2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "9199c0ef",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# dimensions\n",
    "arr2.ndim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e697e6ff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 4)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# shape\n",
    "arr2.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "9a8490aa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# dtype metadata object\n",
    "arr1.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "34d67690",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int32')"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr2.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0f983e8b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# there are a number of functions for creating new arrays\n",
    "# As an examples:\n",
    "# zeros and ones create arrays of 0s or 1s, respectively, with a given length or shape\n",
    "# with a given length\n",
    "np.zeros(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "0aae3d21",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0.]])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# with a given shape\n",
    "np.zeros((3,6))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "00e5b4fb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1.26399697e-311, 1.26399697e-311],\n",
       "        [1.26399697e-311, 8.79345367e+199],\n",
       "        [2.92718921e-311, 0.00000000e+000]],\n",
       "\n",
       "       [[3.25938554e-311, 3.40008234e-308],\n",
       "        [7.46686371e-301, 5.83588819e-302],\n",
       "        [8.90030229e-307, 1.94098787e-307]]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# empty creates an array without initializing its values to any paticular value.\n",
    "# to create a higher dimensional array with these methods,pass a tuple for the shape\n",
    "np.empty((2, 3, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "8d87374b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# arange is an array-valued version of the built-in Python range function.\n",
    "np.arange(15)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "ee6bbd25",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "range(0, 15)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "range(15)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "f068179a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Since Numpy is focused on numerical computing, the datatype, if not specified, will in many cases be float64."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "107f15a7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Data Types for ndarray\n",
    "# The data type or dtype is a special object containing the information the ndarray needs to interpret\n",
    "# a chunk of memory as a paticular type of data.\n",
    "arr1 = np.array([1,2,3], dtype=np.float64)\n",
    "arr2 = np.array([1,2,3], dtype=np.int32)\n",
    "arr1.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "810aca34",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int32')"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr2.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "2f0b1d78",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int32')"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# case an array from one dtype to another using ndarray's astype method:\n",
    "arr = np.array([1,2,3,4,5])\n",
    "arr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "9b656d05",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 2., 3., 4., 5.])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "float_arr = arr.astype(np.float64)\n",
    "float_arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "8e9057c5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "float_arr.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "ddb1abe1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 3.7, -1.2, -2.6,  0.5, 12.9, 10.1])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "7da6964e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 3, -1, -2,  0, 12, 10])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# case some floating-point numbers to be of integer dtype, the decimal part will be truncated.\n",
    "arr.astype(np.int32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "1882b782",
   "metadata": {},
   "outputs": [],
   "source": [
    "# convert an array of strings representing numbers to numeric form.\n",
    "numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "49b9279a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.25, -9.6 , 42.  ])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "numeric_strings.astype(float)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "9a955f25",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "int_array = np.arange(10)\n",
    "calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)\n",
    "int_array.astype(calibers.dtype)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "19bf123f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Arithmetic with Numpy Arrays\n",
    "# Arrays enable you to express batch operations on data without writing any for loops.\n",
    "# Numpy user call this vectorization\n",
    "# Any arithmetic operations between equal-size arrays applies the operation element-wise.\n",
    "arr = np.array([[1., 2., 3.], [4., 5., 6.]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "d05e5942",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 2., 3.],\n",
       "       [4., 5., 6.]])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "e1629357",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.,  4.,  9.],\n",
       "       [16., 25., 36.]])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr * arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "eb6df639",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0.],\n",
       "       [0., 0., 0.]])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr - arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "4166e795",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.        , 1.41421356, 1.73205081],\n",
       "       [2.        , 2.23606798, 2.44948974]])"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr ** 0.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "7d16c49a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.,  4.,  1.],\n",
       "       [ 7.,  2., 12.]])"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Comparisons between arrays of the same size yield boolean arrays.\n",
    "arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])\n",
    "arr2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "409d19e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False,  True, False],\n",
       "       [ True, False,  True]])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr2 > arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "219379d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluating operations between differently sized arrays is called broadcasting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "ab1d92bd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Basic indexing and Slicing\n",
    "# One dimensional arrays\n",
    "arr = np.arange(10)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "9322b7a5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr[5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "59c97a7a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 6, 7])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr[5:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "e731a613",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# assign a scalar value to a slice\n",
    "arr[5:8] = 12\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "058bc382",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([12, 12, 12])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# array slices are views on the original array\n",
    "# This means the data is not copied,and any modifications to the view will be reflected in the source array.\n",
    "arr_slice = arr[5:8]\n",
    "arr_slice"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "3318bdc4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([    0,     1,     2,     3,     4,    12, 12345,    12,     8,\n",
       "           9])"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# change the values in arr_slice\n",
    "arr_slice[1] = 12345\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "05d9bb76",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# The \"bare\" slice [:] will assign to all values in an array\n",
    "arr_slice[:] = 64\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "ec175146",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([64, 64, 64])"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# explicitly copy the array\n",
    "arr[5:8].copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "fba32c8d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([7, 8, 9])"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# with higher dimensional arrays\n",
    "# In a tow-dimensional array, the elements at each index are no longer scalars but rather one-dimensional arrays\n",
    "arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "arr2d[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "d622cb70",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# pass a comma-seprated list of indices to select individual elements\n",
    "arr2d[0][2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "f5b89e6e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr2d[0,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "9cb1e08d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# axis 0 as the \"rows\" of the array\n",
    "# axis 1 as the \"columns\"\n",
    "arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9],\n",
    "[10, 11, 12]]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "2fb6ec65",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 1,  2,  3],\n",
       "        [ 4,  5,  6]],\n",
       "\n",
       "       [[ 7,  8,  9],\n",
       "        [10, 11, 12]]])"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 2 x 2 x 3 array arr3d\n",
    "arr3d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "aee87f00",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# if you omit later indices, the returned object will be a lower dimensional ndarray \n",
    "# consisting of all the data along the higher dimensions.\n",
    "# arr3d[0] is a 2 x 3 array\n",
    "arr3d[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "3f62dd46",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[42, 42, 42],\n",
       "        [42, 42, 42]],\n",
       "\n",
       "       [[ 7,  8,  9],\n",
       "        [10, 11, 12]]])"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Both scalar values and arrays can be assigned to arr3d[0]\n",
    "old_values = arr3d[0].copy()\n",
    "arr3d[0] = 42\n",
    "arr3d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "2f255295",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 1,  2,  3],\n",
       "        [ 4,  5,  6]],\n",
       "\n",
       "       [[ 7,  8,  9],\n",
       "        [10, 11, 12]]])"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr3d[0] = old_values\n",
    "arr3d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "39452eaa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([7, 8, 9])"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# arr3d[1, 0] gives you all of the values whose indices start with (1,0), forming a 1-dimensional array\n",
    "arr3d[1, 0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "ec5f1e1b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([7, 8, 9])"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# this expression is the same as though we had indexed in two steps:\n",
    "x = arr3d[1]\n",
    "x[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "679c4535",
   "metadata": {},
   "outputs": [],
   "source": [
    "# all of these cases where subsections of the array have been selected.\n",
    "# the returned arrays are views"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "4ca043d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Indexing with slices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "64dd69b4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Like 1-dimensional objects such as Python lists, ndarrays can be sliced with the familiar syntax\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "77199cf0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1,  2,  3,  4, 64])"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr[1:6]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "1a1f3b37",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6],\n",
       "       [7, 8, 9]])"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# slicing 2-dimensional array\n",
    "arr2d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "0858addd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# it has sliced along axis 0, the fist axis.\n",
    "# A slice, selects a range of elements along an axis.\n",
    "# It can be helpfull to read the expression arr2d[:2] as \"select the first tow rows of arr2d.\"\n",
    "# :2 is a slice\n",
    "arr2d[:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "41d7657c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[2, 3],\n",
       "       [5, 6]])"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# you can pass multiple slices, just like you can pass multiple indexes\n",
    "# 你可以像传入多个indices一样，传入多个slices\n",
    "# :2, 1: area slices.\n",
    "arr2d[:2, 1:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "03a14404",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([4, 5])"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# when slicing like this, you always obtain array views of the same number of dimensions.\n",
    "# By mixing integer indexes and slices, you get lower dimensional slices.\n",
    "# select the second row but only the first two columns\n",
    "arr2d[1, :2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "f0059ffe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([3, 6])"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# select third column but only the first rows\n",
    "arr2d[:2, 2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "e02949d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1],\n",
       "       [4],\n",
       "       [7]])"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a colon by itself means to task the entire axis\n",
    "# you can slice only higher dimentional axes(refer to other axes expect axis 0) by doing\n",
    "arr2d[:, :1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "51d98cfa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 0, 0],\n",
       "       [4, 0, 0],\n",
       "       [7, 8, 9]])"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# assigning to a slice expression assigns to the whole selection（对一个切片表达式赋值时，赋值会作用于\n",
    "# 整个被选中的区域）\n",
    "# slice expression（切片表达式）：arr[1:3]、arr2d[:2,1:]\n",
    "arr2d[:2, 1:] = 0\n",
    "arr2d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "84e08f87",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Bollean indexing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "3cd95547",
   "metadata": {},
   "outputs": [],
   "source": [
    "# an array of names with duplicates\n",
    "names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will','Joe', 'Joe'])\n",
    "data = np.random.randn(7,4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "0828101b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "f4641f02",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.43846773,  0.18956821,  0.4175724 ,  1.57600817],\n",
       "       [-0.95865206,  0.472943  ,  0.55124354,  2.05416347],\n",
       "       [ 1.15440751, -0.00905985,  0.55491577,  0.03387261],\n",
       "       [ 0.53563068, -1.6806497 ,  0.5130208 , -0.03998202],\n",
       "       [ 0.73401036, -0.09471329,  0.87027303,  1.23100134],\n",
       "       [ 0.19736036, -0.3892609 ,  0.30022932,  0.48137861],\n",
       "       [-1.96485583, -1.01254252,  0.82017416,  2.20028368]])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "5529b1d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False, False,  True, False, False, False])"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Comparing names with the string 'Bob' yields a boolean array\n",
    "names == 'Bob'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "bdfd7adb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.43846773,  0.18956821,  0.4175724 ,  1.57600817],\n",
       "       [ 0.53563068, -1.6806497 ,  0.5130208 , -0.03998202]])"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# boolean array can be passed when indexing the array\n",
    "# the boolean array must be the same length as the array axis it's indexing\n",
    "data[names == 'Bob']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "275d3c0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.4175724 ,  1.57600817],\n",
       "       [ 0.5130208 , -0.03998202]])"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# mix and match (混合搭配) boolean arrays with slices or integers\n",
    "# select from the rows where names== 'Bob' and index the columns,too\n",
    "data[names=='Bob', 2:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "4e2aacb6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.57600817, -0.03998202])"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[names == 'Bob', 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "65679e83",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False,  True,  True, False,  True,  True,  True])"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# select everything but 'Bob', use != or negate the condition using ~\n",
    "names != 'Bob'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "03260c17",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False,  True,  True, False,  True,  True,  True])"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "~(names == 'Bob')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "1423626e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.95865206,  0.472943  ,  0.55124354,  2.05416347],\n",
       "       [ 1.15440751, -0.00905985,  0.55491577,  0.03387261],\n",
       "       [ 0.73401036, -0.09471329,  0.87027303,  1.23100134],\n",
       "       [ 0.19736036, -0.3892609 ,  0.30022932,  0.48137861],\n",
       "       [-1.96485583, -1.01254252,  0.82017416,  2.20028368]])"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[~(names == 'Bob')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "bfbb0773",
   "metadata": {},
   "outputs": [],
   "source": [
    "# the ~ operator can be useful when you want to invert a boolean array referenced by a variable\n",
    "cond = names == 'Bob'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "aa7c15e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.95865206,  0.472943  ,  0.55124354,  2.05416347],\n",
       "       [ 1.15440751, -0.00905985,  0.55491577,  0.03387261],\n",
       "       [ 0.73401036, -0.09471329,  0.87027303,  1.23100134],\n",
       "       [ 0.19736036, -0.3892609 ,  0.30022932,  0.48137861],\n",
       "       [-1.96485583, -1.01254252,  0.82017416,  2.20028368]])"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[~cond]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "f97333fc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False,  True,  True,  True, False, False])"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# selecting tow of three names to combine multiple boolean conditions\n",
    "# use boolean arithmeetic operators like &(and) and |(or)\n",
    "mask = (names == 'Bob') | (names == 'Will')\n",
    "mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "b868f6c4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.43846773,  0.18956821,  0.4175724 ,  1.57600817],\n",
       "       [ 1.15440751, -0.00905985,  0.55491577,  0.03387261],\n",
       "       [ 0.53563068, -1.6806497 ,  0.5130208 , -0.03998202],\n",
       "       [ 0.73401036, -0.09471329,  0.87027303,  1.23100134]])"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[mask]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "7e14e13c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# selecting data from an array by boolean indexing always creates a copy of the data, even if the returned array\n",
    "# is unchange.\n",
    "# Python keywords \"and\" and \"or\" do not work with boolean arrays, Use & and | instead."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "31c1b8a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Setting values with boolean arrays\n",
    "# works by subsitueing the value or vallues one the right hand side into the locations where the boolean\n",
    "# array's values are True\n",
    "data[data < 0] = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "bc7f6444",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1.43846773, 0.18956821, 0.4175724 , 1.57600817],\n",
       "       [0.        , 0.472943  , 0.55124354, 2.05416347],\n",
       "       [1.15440751, 0.        , 0.55491577, 0.03387261],\n",
       "       [0.53563068, 0.        , 0.5130208 , 0.        ],\n",
       "       [0.73401036, 0.        , 0.87027303, 1.23100134],\n",
       "       [0.19736036, 0.        , 0.30022932, 0.48137861],\n",
       "       [0.        , 0.        , 0.82017416, 2.20028368]])"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "515898c9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Setting whole rows or columns using a one-dimensional boolean array\n",
    "data[names != 'Joe'] = 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "c3c5c653",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[7.        , 7.        , 7.        , 7.        ],\n",
       "       [0.        , 0.472943  , 0.55124354, 2.05416347],\n",
       "       [7.        , 7.        , 7.        , 7.        ],\n",
       "       [7.        , 7.        , 7.        , 7.        ],\n",
       "       [7.        , 7.        , 7.        , 7.        ],\n",
       "       [0.19736036, 0.        , 0.30022932, 0.48137861],\n",
       "       [0.        , 0.        , 0.82017416, 2.20028368]])"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "70db78ad",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0., 0.],\n",
       "       [1., 1., 1., 1.],\n",
       "       [2., 2., 2., 2.],\n",
       "       [3., 3., 3., 3.],\n",
       "       [4., 4., 4., 4.],\n",
       "       [5., 5., 5., 5.],\n",
       "       [6., 6., 6., 6.],\n",
       "       [7., 7., 7., 7.]])"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Fancy Indexing\n",
    "# Fancy indexing is a term adopted by Numpy to describe indexing using integer array.\n",
    "# we had an 8 x 4 array\n",
    "arr = np.empty((8,4))\n",
    "for i in range(8):\n",
    "    arr[i] = i\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "ef4bd0cc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4., 4., 4., 4.],\n",
       "       [3., 3., 3., 3.],\n",
       "       [0., 0., 0., 0.],\n",
       "       [6., 6., 6., 6.]])"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# to select out a subset of the rows in a particular order\n",
    "# you can pass a list or ndarray of integers specifying the desired order\n",
    "arr[[4,3,0,6]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "b1b036f6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[5., 5., 5., 5.],\n",
       "       [3., 3., 3., 3.],\n",
       "       [1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Using negative indices selects rows from the end\n",
    "arr[[-3,-5,-7]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "6fe0c760",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3],\n",
       "       [ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11],\n",
       "       [12, 13, 14, 15],\n",
       "       [16, 17, 18, 19],\n",
       "       [20, 21, 22, 23],\n",
       "       [24, 25, 26, 27],\n",
       "       [28, 29, 30, 31]])"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr = np.arange(32).reshape((8,4))\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "2013ee52",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4, 23, 29, 10])"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Passing multiple index arrays\n",
    "# it selects a one-dimensional array of elements corresponding to each tuple of indices.\n",
    "# Here the elements (1,0) (5,3) (7,1) (2,2) were selected.\n",
    "arr[[1,5,7,2],[0,3,1,2]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "5f385e8e",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3,  4],\n",
       "       [ 5,  6,  7,  8,  9],\n",
       "       [10, 11, 12, 13, 14]])"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Transposing Arrays and Swapping Axes (转置数组、交换轴)\n",
    "# transposing is a special form of  reshaping that \n",
    "# similary returns a view on the underlying data without copying anything\n",
    "# Arrays have the transpose method and also the special T attribute\n",
    "arr = np.arange(15).reshape((3,5))\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "1ef8a562",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  5, 10],\n",
       "       [ 1,  6, 11],\n",
       "       [ 2,  7, 12],\n",
       "       [ 3,  8, 13],\n",
       "       [ 4,  9, 14]])"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "id": "85fa462a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.82947603,  0.09998679,  1.43012641],\n",
       "       [-0.80024522, -1.42672799,  0.03254865],\n",
       "       [-0.20088415, -1.2283614 , -0.90882084],\n",
       "       [ 0.46553255, -0.3862384 ,  0.02728766],\n",
       "       [-2.27026501,  2.36827394,  1.68872214],\n",
       "       [-0.94087576, -0.68957837,  1.71999542]])"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# when doing matrix computations, you may do this very often - for example\n",
    "# when computing the inner matrix product using np.dot\n",
    "arr = np.random.randn(6,3)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "f1de48b8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 7.62484831, -3.60205448, -6.46918028],\n",
       "       [-3.60205448,  9.78784173,  4.01566158],\n",
       "       [-6.46918028,  4.01566158,  8.68318763]])"
      ]
     },
     "execution_count": 100,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.dot(arr.T, arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "92f26af7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 7.62484831, -3.60205448, -6.46918028],\n",
       "       [-3.60205448,  9.78784173,  4.01566158],\n",
       "       [-6.46918028,  4.01566158,  8.68318763]])"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# the @ infix operator is another way to do matrix multiplication\n",
    "arr.T @ arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "id": "f80355b3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7]],\n",
       "\n",
       "       [[ 8,  9, 10, 11],\n",
       "        [12, 13, 14, 15]]])"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# For higer dimensional arrays, transpose will accept a tuple of axis numbers to permute the axes;\n",
    "arr = np.arange(16).reshape((2,2,4))\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "id": "05143413",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0,  1,  2,  3],\n",
       "        [ 8,  9, 10, 11]],\n",
       "\n",
       "       [[ 4,  5,  6,  7],\n",
       "        [12, 13, 14, 15]]])"
      ]
     },
     "execution_count": 103,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Here, the axes have been reordered with \n",
    "# the second axis first the first axis second, and the last axis unchanged\n",
    "# 每个元素的位置坐标发生变换\n",
    "arr.transpose((1,0,2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "id": "541f3c16",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7]],\n",
       "\n",
       "       [[ 8,  9, 10, 11],\n",
       "        [12, 13, 14, 15]]])"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Simple transposing with .T is a special case of swapping axes.\n",
    "# ndarray has the method swapaxes, which tasks a pair of axis numbers and switches the indicated axes\n",
    "# to rearrange the data\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "id": "091b65cc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0,  4],\n",
       "        [ 1,  5],\n",
       "        [ 2,  6],\n",
       "        [ 3,  7]],\n",
       "\n",
       "       [[ 8, 12],\n",
       "        [ 9, 13],\n",
       "        [10, 14],\n",
       "        [11, 15]]])"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.swapaxes(1,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "02afbe57",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 106,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Universal Functions: Fast Element-Wise Array Functions\n",
    "# A universal function, or ufunc, is a function that performs element-wise operations on data in ndarrays.\n",
    "# You can think of them as fast vectorized wrappers for simple functions that take one or more scalar values and\n",
    "# produce one or more scalar results\n",
    "\n",
    "# Many ufuncs are simple element-wise transformations, like sqrt or exp:\n",
    "arr = np.arange(10)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "id": "811bcf9f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,\n",
       "       2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])"
      ]
     },
     "execution_count": 107,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# These are reffered to as unary ufuncs\n",
    "np.sqrt(arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "8fc6021f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,\n",
       "       5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,\n",
       "       2.98095799e+03, 8.10308393e+03])"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.exp(arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "779d332b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ohters, such as add or maximum, take two arrays(thus, binary ufuncs) and return a single array as the result:\n",
    "x = np.random.randn(8)\n",
    "y = np.random.randn(8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "95b4dd77",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-1.31099345,  0.8900275 , -2.45936094,  0.87628025,  0.72021107,\n",
       "       -0.40864423, -0.97439538, -0.40560653])"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "8ed4e7f2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.78317934,  0.0444976 , -0.60968123,  1.07847366,  0.15422433,\n",
       "       -0.33426009,  1.04975147, -0.6533698 ])"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "d595bc31",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.78317934,  0.8900275 , -0.60968123,  1.07847366,  0.72021107,\n",
       "       -0.33426009,  1.04975147, -0.40560653])"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Here, numpy.maximum computed the element-wise maximum of the elements in x and y.\n",
    "np.maximum(x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "id": "134a46b7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.81303919, -1.67757023, -4.623845  , -5.75022859, -0.20011146,\n",
       "       -2.23120075, -0.86056068])"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# While not common, a ufunc can return multiple arrays. modf is one example, a vectorized version of the build-in\n",
    "# Python divmod; it return the fractional and integral parts of a floating-point array\n",
    "arr = np.random.randn(7) * 5\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "id": "e0e98bc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "remainder, whole_part = np.modf(arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "id": "bfcd46e9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.81303919, -0.67757023, -0.623845  , -0.75022859, -0.20011146,\n",
       "       -0.23120075, -0.86056068])"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "remainder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "id": "7c9bff7f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0., -1., -4., -5., -0., -2., -0.])"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "whole_part"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "id": "6d96ef7d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.81303919, -1.67757023, -4.623845  , -5.75022859, -0.20011146,\n",
       "       -2.23120075, -0.86056068])"
      ]
     },
     "execution_count": 118,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ufuncs accept an optional out argument that allows them to assign their results into an existing array\n",
    "# rather than creating a new one\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "id": "82e5d6f9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 0., 0., 0., 0., 0., 0.])"
      ]
     },
     "execution_count": 123,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "out = np.zeros_like(arr)\n",
    "out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "id": "996cc3fe",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.81303919, -0.67757023, -3.623845  , -4.75022859,  0.79988854,\n",
       "       -1.23120075,  0.13943932])"
      ]
     },
     "execution_count": 124,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.add(arr,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "id": "afe9a306",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.81303919, -0.67757023, -3.623845  , -4.75022859,  0.79988854,\n",
       "       -1.23120075,  0.13943932])"
      ]
     },
     "execution_count": 125,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.add(arr, 1, out=out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "id": "c1a76979",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.81303919, -0.67757023, -3.623845  , -4.75022859,  0.79988854,\n",
       "       -1.23120075,  0.13943932])"
      ]
     },
     "execution_count": 126,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "id": "6f0087ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Array-Oriented Programming with Arrays\n",
    "# Using Numpy arrays enables you to express many kinds of data processing tasks as \n",
    "# concise array expression that might otherwise require writing loops\n",
    "\n",
    "# replacing explicit loops with array expressions is referred to by some people vectorization.\n",
    "# vectorized array operations will usually be significantly faster than their pure Python"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "id": "c4c91026",
   "metadata": {},
   "outputs": [],
   "source": [
    "# evaluate the function sqrt(x^2 + y^2) across a regular grid of values.\n",
    "# The np.meshgrid function takes tow 1D arrays and produces two 2D matrices corresponding to all pairs of (x,y)\n",
    "# in two arrays\n",
    "points = np.arange(-5,5,0.01)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "id": "7102eec8",
   "metadata": {},
   "outputs": [],
   "source": [
    "xs, ys = np.meshgrid(points,points)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 131,
   "id": "5d2bb6b1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-5.  , -5.  , -5.  , ..., -5.  , -5.  , -5.  ],\n",
       "       [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],\n",
       "       [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],\n",
       "       ...,\n",
       "       [ 4.97,  4.97,  4.97, ...,  4.97,  4.97,  4.97],\n",
       "       [ 4.98,  4.98,  4.98, ...,  4.98,  4.98,  4.98],\n",
       "       [ 4.99,  4.99,  4.99, ...,  4.99,  4.99,  4.99]])"
      ]
     },
     "execution_count": 131,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "id": "8d7089fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Expressing Conditional Logic as Array Operations\n",
    "# The numpy.where function is a vectorized version of the ternary expression x if condition else y.\n",
    "# we had a boolean array and two arrays of values\n",
    "xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])\n",
    "yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])\n",
    "cond = np.array([True, False, True, True, False])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 134,
   "id": "f8770264",
   "metadata": {},
   "outputs": [],
   "source": [
    "# we want to take a value from xarr whenever the corresponding value in cond is True, and otherwise take the \n",
    "# value from yarr.\n",
    "# A list comprehension doing this might look like\n",
    "result = [(x if c else y) for x,y,c in zip(xarr, yarr,cond)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "id": "1e6ab11f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1.1, 2.2, 1.3, 1.4, 2.5]"
      ]
     },
     "execution_count": 135,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "id": "28d8c23c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.1, 2.2, 1.3, 1.4, 2.5])"
      ]
     },
     "execution_count": 137,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = np.where(cond,xarr,yarr)\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 138,
   "id": "dac76e15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.59154086,  0.93356351,  1.30229764, -1.32525849],\n",
       "       [-0.39523979,  0.48255539,  1.32974372,  0.29367909],\n",
       "       [-1.55480596, -0.66327705,  0.6810346 , -1.03984974],\n",
       "       [-1.74895687, -1.36511067, -0.0849108 , -0.87735349]])"
      ]
     },
     "execution_count": 138,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# A typical use of where in data analysis is to produce a new array of values on another array.\n",
    "# suppose you had a matrix of randomly generated data and you wanted to replace all positive values with 2 and\n",
    "# all negative value with -2.\n",
    "arr = np.random.randn(4, 4)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "id": "99e8580a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ True,  True,  True, False],\n",
       "       [False,  True,  True,  True],\n",
       "       [False, False,  True, False],\n",
       "       [False, False, False, False]])"
      ]
     },
     "execution_count": 139,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr > 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "id": "bce4d98c",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 2,  2,  2, -2],\n",
       "       [-2,  2,  2,  2],\n",
       "       [-2, -2,  2, -2],\n",
       "       [-2, -2, -2, -2]])"
      ]
     },
     "execution_count": 140,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.where(arr > 0, 2, -2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 141,
   "id": "40eced8d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 2.        ,  2.        ,  2.        , -1.32525849],\n",
       "       [-0.39523979,  2.        ,  2.        ,  2.        ],\n",
       "       [-1.55480596, -0.66327705,  2.        , -1.03984974],\n",
       "       [-1.74895687, -1.36511067, -0.0849108 , -0.87735349]])"
      ]
     },
     "execution_count": 141,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# you can combine scalars and arrays when using np.where\n",
    "# replace all positive values in arr with the constant 2\n",
    "np.where(arr > 0, 2, arr) # set only positive values to 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "id": "4e653d0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mathematical and Statistical Methods\n",
    "# a set of mathematical functions that compute statistics about an entire array or about the data along an axis\n",
    "# are accessible as methods of the array class.\n",
    "# You can use aggregations(sometimes called reductions) like sum, mean and std either by calling the array\n",
    "# instance method or using the top-level Numpy function.\n",
    "arr = np.random.randn(5,4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "id": "2d6fd9d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.79355484,  0.9801741 ,  1.5037801 , -0.69653452],\n",
       "       [ 0.57342492, -0.34191349,  0.3540301 , -0.4233698 ],\n",
       "       [ 0.09727019, -0.70525647,  0.06133768, -0.18101156],\n",
       "       [-0.53437206,  1.12550849,  0.11040298,  0.53740877],\n",
       "       [-0.11778663, -0.61035747, -0.03367521, -0.95270512]])"
      ]
     },
     "execution_count": 143,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 144,
   "id": "f6be399a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.0023599926859550592"
      ]
     },
     "execution_count": 144,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 145,
   "id": "f93041ff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.0023599926859550592"
      ]
     },
     "execution_count": 145,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "id": "8b16dbe2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.04719985371910118"
      ]
     },
     "execution_count": 146,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "id": "796018f3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.24846621,  0.04054293, -0.18191504,  0.30973704, -0.42863111])"
      ]
     },
     "execution_count": 147,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Functions like mean and sum take an optional axis argument that computes the statistic over the given axis,\n",
    "# resulting in an array with one less dimension\n",
    "arr.mean(axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "id": "5b438b45",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.77501843,  0.44815516,  1.99587565, -1.71621224])"
      ]
     },
     "execution_count": 148,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# arr.mean(axis=1) means compute mean across the columns\n",
    "# arr.sum(axis=0) means compute sum down the rows\n",
    "arr.sum(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "id": "d3f109b6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  1,  3,  6, 10, 15, 21, 28])"
      ]
     },
     "execution_count": 149,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# other methods like cumsum and cumprod do not arrgegate, instead producing an array of the intermediate results\n",
    "arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])\n",
    "arr.cumsum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "id": "d1c9e0f9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 2],\n",
       "       [3, 4, 5],\n",
       "       [6, 7, 8]])"
      ]
     },
     "execution_count": 150,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# In multidimensional arrays,accumulation functions like cumsum return an array of the same size, \n",
    "# but with the partial aggregates(\"部分聚合值\"，指每个位置的值是到当前位置为止的累计结果) computed along \n",
    "# the indicated axis accroding to each lower dimensional slices（低一维的切片，比如在二维数组中，沿着axis=0累积时\n",
    "#，每个一维切片都会参与计算）\n",
    "arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 151,
   "id": "a0234163",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2],\n",
       "       [ 3,  5,  7],\n",
       "       [ 9, 12, 15]])"
      ]
     },
     "execution_count": 151,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.cumsum(axis=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "id": "18811a83",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  0,   0,   0],\n",
       "       [  3,  12,  60],\n",
       "       [  6,  42, 336]])"
      ]
     },
     "execution_count": 153,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.cumprod(axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "id": "b0d8cb78",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4.63451714e-01,  1.66061139e+00,  2.65613477e-01, -1.20967572e+00,\n",
       "        1.78483601e+00, -1.46741881e+00,  7.26556726e-01,  7.38819194e-01,\n",
       "       -5.11911820e-01, -1.11259556e+00, -1.01748492e-01, -5.68161428e-01,\n",
       "        9.72125151e-01, -1.88226414e-03,  4.58238846e-01, -1.14147604e+00,\n",
       "        1.05236778e+00, -1.06757443e+00,  2.19821002e-01, -1.60934859e+00,\n",
       "        5.98049680e-01, -9.19729703e-01,  9.26572797e-01,  1.31864476e-01,\n",
       "       -4.69141894e-01, -1.94256335e+00,  4.92832948e-01, -5.50579862e-03,\n",
       "        1.03526242e+00,  5.26919893e-01,  9.77699112e-01, -7.15311066e-01,\n",
       "        7.72467532e-01,  5.50862264e-01,  1.08599224e+00, -1.29886497e+00,\n",
       "        3.94453943e-02,  1.05660599e+00,  7.99412534e-02, -8.59578764e-01,\n",
       "       -1.13155805e+00,  1.52430933e-01,  2.00026637e-01, -6.45334299e-01,\n",
       "       -1.19651333e+00,  1.21807107e+00,  4.01521269e-01, -1.82918064e+00,\n",
       "        4.80072902e-01,  6.06378819e-01,  4.47115828e-01, -4.48125940e-01,\n",
       "       -7.98774394e-01, -5.16945619e-01, -3.64820434e-01,  2.32547041e+00,\n",
       "        4.77236373e-01, -1.38594774e+00, -2.59851095e+00,  1.26184525e+00,\n",
       "        6.98482561e-01, -8.61759336e-01, -1.16765054e+00,  9.68987916e-01,\n",
       "        1.36942450e+00,  1.41920751e-01,  2.33424869e+00,  1.57598136e+00,\n",
       "       -6.55788603e-01,  5.02509491e-01, -8.67361038e-03, -1.15620032e+00,\n",
       "       -7.85602585e-01, -7.86908468e-01,  7.94651188e-01,  5.28988566e-01,\n",
       "        2.50818233e+00,  9.59391647e-01,  1.46680696e+00,  3.05841942e-01,\n",
       "       -3.27250661e-01,  1.81198281e-01, -5.12018645e-01,  5.20692885e-01,\n",
       "        5.52340930e-01, -1.62889212e+00,  1.77191337e-01,  6.84704483e-01,\n",
       "        1.71750583e+00,  5.30536755e-02, -1.90237997e-02,  2.38681931e-01,\n",
       "       -3.63154632e-01,  1.71064584e+00,  5.21146330e-01, -8.99628950e-01,\n",
       "       -4.22474993e-01, -1.19835144e+00, -8.40046969e-02, -1.04613039e-02])"
      ]
     },
     "execution_count": 155,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Method for Boolean Arrays\n",
    "# sum is often used as a means of counting True values in a boolean array\n",
    "arr = np.random.randn(100)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "id": "956f0617",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "56"
      ]
     },
     "execution_count": 156,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Number of positive values\n",
    "(arr > 0).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "id": "9c77d94b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "44"
      ]
     },
     "execution_count": 157,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Number of no-positive values\n",
    "(arr <=0).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "id": "e7960e18",
   "metadata": {},
   "outputs": [],
   "source": [
    "# There are two additional methods, any and all, useful especially for boolean arrays.\n",
    "# any tests whether one or more values in an array is True\n",
    "# all check if every value is True\n",
    "bools = np.array([False, False, True, False])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "id": "06e7b7b7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 159,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bools.any()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "a27d5135",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 160,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bools.all()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "id": "d0e57143",
   "metadata": {},
   "outputs": [],
   "source": [
    "# These methods also work with non-boolean arrays, where non-zero elements are treated as True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "id": "ba601f79",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.3167847 ,  0.59406524, -1.01971809,  1.74191355, -1.82389247,\n",
       "        1.2752027 ])"
      ]
     },
     "execution_count": 162,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Sorting\n",
    "# Like Python's build-in list type, Numpy arrays can be sorted in-place with the sort method\n",
    "arr = np.random.randn(6)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "id": "b7b50969",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.sort()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "id": "b65aebd7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-1.82389247, -1.01971809, -0.3167847 ,  0.59406524,  1.2752027 ,\n",
       "        1.74191355])"
      ]
     },
     "execution_count": 164,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "id": "eda84849",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.54336695,  2.77728565, -0.5626039 ],\n",
       "       [ 1.95898639, -1.06223086,  0.54344089],\n",
       "       [-1.31247323, -0.22577963,  0.39770953],\n",
       "       [-3.22988745,  0.15406419,  0.87796692],\n",
       "       [ 2.01453799,  1.41025936,  1.56026008]])"
      ]
     },
     "execution_count": 166,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# You can sort each one-dimensional section of values in a multidimensional array \n",
    "# in-place (原数组改变不返回新数组) along an axis by passing the axis number to sort:\n",
    "arr = np.random.randn(5,3)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "id": "390f930f",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr.sort(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "id": "3be7865e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.5626039 ,  1.54336695,  2.77728565],\n",
       "       [-1.06223086,  0.54344089,  1.95898639],\n",
       "       [-1.31247323, -0.22577963,  0.39770953],\n",
       "       [-3.22988745,  0.15406419,  0.87796692],\n",
       "       [ 1.41025936,  1.56026008,  2.01453799]])"
      ]
     },
     "execution_count": 168,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 172,
   "id": "39313735",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-10,  -3,   0,   1,   5,   7])"
      ]
     },
     "execution_count": 172,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# The top-level method(在numpy库里，一般指直接定义在模块最外层的函数，不依附于某个对象或类)\n",
    "# 也就是说：\n",
    "# 你是通过模块名调用的：np.sort()\n",
    "# 而不是通过对象调用的：arr.sort()\n",
    "# The top-level method np.sort returns a sorted copy of an array instead of modifying the array in-place.\n",
    "arr2 = np.array([5, -10, 7, 1, 0, -3])\n",
    "sorted_arr2 = np.sort(arr2)\n",
    "sorted_arr2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "id": "c73c5546",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['Bob', 'Joe', 'Will'], dtype='<U4')"
      ]
     },
     "execution_count": 173,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Unique and Other Set Logic\n",
    "# NumPy has some basic set operations for one-dimensional ndarrays.\n",
    "# A commonly used one is np.unique, which returns the sorted unique values in an array\n",
    "names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will','Joe', 'Joe'])\n",
    "np.unique(names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 174,
   "id": "b98e539c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3, 4])"
      ]
     },
     "execution_count": 174,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])\n",
    "np.unique(ints)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 175,
   "id": "b89b6c22",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Bob', 'Joe', 'Will']"
      ]
     },
     "execution_count": 175,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Contrast np.unique with the pure Python alternative\n",
    "sorted(set(names))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "id": "3b75b221",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False, False,  True,  True, False,  True])"
      ]
     },
     "execution_count": 176,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Another function,np.in1d, tests membership of the values in one array in another,returning a boolean array\n",
    "values = np.array([6, 0, 0, 3, 2, 5, 6])\n",
    "np.in1d(values,[2,3,6])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "id": "97d3dbba",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Linear Algebra"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 177,
   "id": "670b70f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Linear Algebra operations, like matrix multiplication, decompositions, determinations, and other square matrix\n",
    "# math, are an important part of many array libraries.Unlike some languages like MATLAB, multiplying two two-di\n",
    "# mensional arrays with * is an element-wise product instead of a matrix dot product. Thus, there is a function\n",
    "# dot, both an array method and a function in the numpy namespace, for matrix multiplication:\n",
    "x = np.array([[1., 2., 3.], [4., 5., 6.]])\n",
    "y = np.array([[6., 23.], [-1, 7], [8, 9]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "id": "fbaa22d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 2., 3.],\n",
       "       [4., 5., 6.]])"
      ]
     },
     "execution_count": 178,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "id": "ad975e62",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 6., 23.],\n",
       "       [-1.,  7.],\n",
       "       [ 8.,  9.]])"
      ]
     },
     "execution_count": 179,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "id": "0c5710d5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 28.,  64.],\n",
       "       [ 67., 181.]])"
      ]
     },
     "execution_count": 180,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.dot(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "id": "e51ef662",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 28.,  64.],\n",
       "       [ 67., 181.]])"
      ]
     },
     "execution_count": 182,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# x.doty(y) is equivalent to np.dot(x,y)\n",
    "np.dot(x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 183,
   "id": "5d8ff272",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6., 15.])"
      ]
     },
     "execution_count": 183,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# A matrix product between a two-dimensional array and a suitably sized one-dimensional array results in \n",
    "# a one-dimensional array\n",
    "np.dot(x,np.ones(3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "id": "d3e7cd44",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6., 15.])"
      ]
     },
     "execution_count": 184,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# the @ symble also works as an infix operator that performs matrix multiplication\n",
    "x @ np.ones(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 185,
   "id": "bd958828",
   "metadata": {},
   "outputs": [],
   "source": [
    "# numpy.linalg has a standard set of matrix decompositions and things like inverse and determinant.\n",
    "from numpy.linalg import inv, qr\n",
    "X = np.random.randn(5,5)\n",
    "mat = X.T.dot(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 186,
   "id": "88d3bd21",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.18596391, -0.92239264, -0.62344227, -0.69565137, -0.91058171],\n",
       "       [-0.92239264,  4.32190393, -1.2601969 ,  0.21934988, -1.09592103],\n",
       "       [-0.62344227, -1.2601969 ,  3.32863053,  2.35762578,  0.7768245 ],\n",
       "       [-0.69565137,  0.21934988,  2.35762578,  3.05698254, -1.40778531],\n",
       "       [-0.91058171, -1.09592103,  0.7768245 , -1.40778531,  9.86234201]])"
      ]
     },
     "execution_count": 186,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mat"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 187,
   "id": "759307a0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.66693548,  0.4823867 ,  0.28454549,  0.22531992,  0.21726065],\n",
       "       [ 0.4823867 ,  0.48824107,  0.46001038, -0.26889954,  0.02417539],\n",
       "       [ 0.28454549,  0.46001038,  1.33961322, -1.085725  , -0.18310814],\n",
       "       [ 0.22531992, -0.26889954, -1.085725  ,  1.35960544,  0.27051687],\n",
       "       [ 0.21726065,  0.02417539, -0.18310814,  0.27051687,  0.17717906]])"
      ]
     },
     "execution_count": 187,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inv(mat)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "id": "68b6990f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "723 ms ± 22.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
     ]
    }
   ],
   "source": [
    "# Pseudorandom Number Generation 伪随机数生成\n",
    "from random import normalvariate\n",
    "N = 1000000\n",
    "%timeit samples = [normalvariate(0,1) for _ in range(N)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 189,
   "id": "80a7480c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "21.1 ms ± 1.98 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "%timeit np.random.normal(size=N)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "id": "eac3963a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We say that these are 伪随机数 because they are generated by an algorithm with 确定性行为 based on the seed\n",
    "# of the random number generator. You can change NumPy's random number generation seed using np.random.seed\n",
    "np.random.seed(1234)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 191,
   "id": "8151fa6b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.19151945, 0.62210877, 0.43772774, 0.78535858, 0.77997581,\n",
       "       0.27259261, 0.27646426, 0.80187218, 0.95813935, 0.87593263])"
      ]
     },
     "execution_count": 191,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# The data generation functions in numpy.random use a global random seed.\n",
    "# To avoid global state, you can use numpy.random.RandomState to create a random number generator isolated\n",
    "# from others.\n",
    "# random number generator\n",
    "rng = np.random.RandomState(1234)\n",
    "rng.rand(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "id": "02411ed9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example: Random Walks\n",
    "# a simple walk starting at 0 with steps of 1 and -1 occuring with equal probability.\n",
    "# Pure Python way to implement a single random walk with 1000 steps using the build-in random module\n",
    "import random\n",
    "position = 0\n",
    "walk = [position]\n",
    "steps = 1000\n",
    "for i in range(steps):\n",
    "    step = 1 if random.randint(0,1) else -1\n",
    "    position += step\n",
    "    walk.append(position)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "1a204485",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x253b19ee130>]"
      ]
     },
     "execution_count": 196,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt \n",
    "plt.plot(walk[:100])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "id": "c56f5a6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# use the np.random module to draw 1000 coin flips at once, set these to 1 and -1, and compute the cumulatie sum\n",
    "nsteps = 1000\n",
    "draws = np.random.randint(0,2,size=nsteps)\n",
    "steps = np.where(draws > 0, 1, -1)\n",
    "walk = steps.cumsum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 219,
   "id": "93e9ca7e",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1000,)"
      ]
     },
     "execution_count": 219,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "steps.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 220,
   "id": "ac09e81e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-15"
      ]
     },
     "execution_count": 220,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# from this we begin to extract statistics like the minimum and maxmum value along the walk's trajectory.\n",
    "walk.min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 221,
   "id": "e6f487a7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "19"
      ]
     },
     "execution_count": 221,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walk.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 222,
   "id": "64373631",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "159"
      ]
     },
     "execution_count": 222,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a more complicated statistic is the first crossing time, the step at which the random walk reaches a paticular\n",
    "# value. Here we might want to know how long it took the random walk to get at least 10 steps away from the\n",
    "# origin 0 in either direction. np.abs(walk) >= 10 gives us a boolean array indicating where the walk has reached\n",
    "# or exceeded 10, but we want the index of the first 10 or -10. we can compute this using argmax, where return\n",
    "# the first index of the maximum value in the boolean array（true is the maximum value）\n",
    "(np.abs(walk) >= 10).argmax()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 223,
   "id": "dd041964",
   "metadata": {},
   "outputs": [],
   "source": [
    "# simulating many random walks at once\n",
    "# if your goal wa to simulate many random walks,say 5000 of them, you can generate all of the random walks with\n",
    "# minor modifications to the preceding code. if passed a 2-tuple, the numpy.randoom functions will generate a \n",
    "# two-dimensinal array of draws, and we can compute the cumulative sum for each row to compute all 5000 random\n",
    "# walks in one shot:\n",
    "nwalks = 5000\n",
    "nsteps = 1000\n",
    "draws = np.random.randint(0,2,size=(nwalks,nsteps))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 224,
   "id": "11d7a669",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5000, 1000)"
      ]
     },
     "execution_count": 224,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "draws.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 225,
   "id": "52d01ca0",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5000, 1000)"
      ]
     },
     "execution_count": 225,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "steps = np.where(draws > 0, 1, -1)\n",
    "steps.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 226,
   "id": "b7fb4ae8",
   "metadata": {},
   "outputs": [],
   "source": [
    "walks = steps.cumsum(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 227,
   "id": "9e35f93c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ -1,  -2,  -1, ..., -18, -19, -18],\n",
       "       [  1,   0,  -1, ...,  24,  23,  22],\n",
       "       [ -1,   0,   1, ...,  14,  15,  16],\n",
       "       ...,\n",
       "       [ -1,   0,  -1, ...,  30,  29,  30],\n",
       "       [  1,   0,  -1, ...,  18,  19,  20],\n",
       "       [ -1,   0,   1, ...,  -6,  -7,  -8]])"
      ]
     },
     "execution_count": 227,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 228,
   "id": "d6769df2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "137"
      ]
     },
     "execution_count": 228,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# now we can compute the maximum and minimum values obtained over all of the walks\n",
    "walks.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 230,
   "id": "e7b9ae29",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-137"
      ]
     },
     "execution_count": 230,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walks.min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 231,
   "id": "1307a637",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False, False, False, ..., False, False, False],\n",
       "       [False, False, False, ..., False, False, False],\n",
       "       [False, False, False, ..., False, False, False],\n",
       "       ...,\n",
       "       [False, False, False, ...,  True, False,  True],\n",
       "       [False, False, False, ..., False, False, False],\n",
       "       [False, False, False, ..., False, False, False]])"
      ]
     },
     "execution_count": 231,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.abs(walks) >= 30"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "201f6434",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 232,
   "id": "c314419b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Out of these walks, let's compute the minimum crossing time to 30 or -30. this is 稍微棘手 because not all\n",
    "# 5000 of them reach 30. we can check this using the any method\n",
    "hits30 = (np.abs(walks) >= 30).any(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 234,
   "id": "1916c843",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5000,)"
      ]
     },
     "execution_count": 234,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hits30.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 235,
   "id": "13d69509",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3431"
      ]
     },
     "execution_count": 235,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Number that hit 30 or -30\n",
    "hits30.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 247,
   "id": "0fc036bc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3431, 1000)"
      ]
     },
     "execution_count": 247,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(np.abs(walks[hits30]) >= 30).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 244,
   "id": "b1c46793",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can use this boolean array to select out the rows of walks that actually cross the absolute 30 level and \n",
    "# call argmax across axis 1 to get the crossing times\n",
    "crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 251,
   "id": "5b46ec3f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([267, 989, 923, ..., 937, 575, 197], dtype=int64)"
      ]
     },
     "execution_count": 251,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "crossing_times"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 253,
   "id": "82b07e65",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "506.2532789274264"
      ]
     },
     "execution_count": 253,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Lastly, we compute the average minimum crossing time\n",
    "crossing_times.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 255,
   "id": "9429deef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Feel free to experiment with other distributions for the steps other than equal-sized coin flips.\n",
    "# You need only use a different random number generation function, like normal to generate normally distributed\n",
    "# steps with some mean and standard deviation\n",
    "steps = np.random.normal(loc=0, scale=0.25, size=(nwalks, nsteps))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c4d24db",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
